home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_400
/
432_01
/
ptmid3
/
ptmidsav.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-07-02
|
15KB
|
475 lines
/*
* ptmidsav.c: MOD-format saver module for ptmid. Takes a structure
* representing a tune and creates a file which contains that tune.
*
* Author: Andrew Scott (c)opyright 1994
*
* Date: 17/11/1993 ver 0.0
* 8/1/1994 ver 0.1
* 11/2/1994 ver 0.2
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "ptmid.h"
#include "samples.h"
/**
** The midiperiod array allows quick conversion between MIDI note
** values and Protracker note values. All "out-of-bounds" values
** are given closest legal value.
**/
unsigned midiperiod[128] = {
0, 1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
1712,1616,1525,1440,1357,1281,1209,1141,1077,1017,961, 907,
856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57,
57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
57, 57, 57, 57, 57, 57, 57, 57
};
/*
* PutblanksPfile: Outputs as many null characters to a given file as
* is desired (max. 131). Inelegant solution.
*/
void PutblanksPfile(FILE *pfile, int cch)
{
if (cch)
fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0", 1, cch, pfile);
}
/*
* PutPfileSz: Outputs a string to a given file and pads it to the correct
* length by outputting null characters if necessary.
*/
void PutPfileSz(FILE *pfile, Sz szT, int cch)
{
while (*szT && cch) {
putc(*(szT++), pfile);
cch--;
}
PutblanksPfile(pfile, cch);
}
/*
* WritePfile: Writes division information (sample, pitch, effect) to
* given file in the format specified by wModfmt.
*
* date: 2/7/1994 - added multi-format support
* 3/7/1994 - now aborts on write error
*/
void WritePfile(FILE *pfile, unsigned bSam, unsigned wPit, unsigned wEff)
{
static int cNote = -1, irgchPos, Buffsiz;
static char *pchBuff;
if (-1 == cNote) {
irgchPos = 0;
if (1 == wModfmt)
pchBuff = (char *) malloc(cNote = Buffsiz = 4 * wMaxchan * DIVSPERPAT);
else
pchBuff = (char *) malloc(cNote = Buffsiz = 3 * wMaxchan * DIVSPERPAT);
}
switch (wModfmt) {
case 1:
wPit = midiperiod[wPit];
pchBuff[0 + irgchPos] = (bSam & 0xF0) | (wPit >> 8);
pchBuff[1 + irgchPos] = wPit & 0xFF;
pchBuff[2 + irgchPos] = ((bSam & 0x0F) << 4) | (wEff >> 8);
pchBuff[3 + irgchPos] = wEff & 0xFF;
cNote -= 4;
irgchPos += 4;
break;
case 2:
if (0 < wPit)
wPit = wPit - 36;
pchBuff[0 + irgchPos] = ((bSam & 0x30) >> 4) | (wPit << 2);
pchBuff[1 + irgchPos] = ((bSam & 0x0F) << 4) | (wEff >> 8);
pchBuff[2 + irgchPos] = wEff & 0xFF;
cNote -= 3;
if ((irgchPos += DIVSPERPAT * 3) >= Buffsiz)
irgchPos -= Buffsiz - 3;
break;
}
if (0 == cNote) {
if (0 == fwrite(pchBuff, Buffsiz, 1, pfile)) {
ERROR;
exit(1);
}
irgchPos = 0;
cNote = Buffsiz;
}
}
/*
* PeiNextPtune: Given a pointer to a tune, will start using it if not
* already using one. Will return next event in quanta, else NULL.
* If it gets to the end of the tune, will set flag to false and will
* wait to be given a new tune.
*/
EI *PeiNextPtune(Tune *ptuneMain, int *pf)
{
static Tune *ptune = NULL;
static unsigned long quant;
EI *pei;
if (NULL == ptune) { /** If first time called **/
if (NULL == ptuneMain) /** If no tune given, quit **/
return NULL;
*pf = 1;
quant = 0;
ptune = ptuneMain; /** Initialize tune pointer to start of tune **/
} else /** Else **/
quant++; /** Advance along tune 1 quantize fraction **/
if (quant < ptune->count) /** If haven't reached next event **/
return NULL; /** return nothing-happening **/
pei = ptune->pei; /** Otherwise note next event **/
if ((ptune = ptune->ptune) == NULL) /** If no more events **/
*pf = 0; /** register end of tune **/
return pei; /** Return that event **/
}
/*
* Convqpm: Converts a given qpm number into a double tempo thingy.
*/
void Convqpm(unsigned qpm, int rgbTempo[2], int ticks)
{
if (792 / ticks <= qpm && 6144 / ticks - 1 >= qpm) {
rgbTempo[0] = ticks; /** If can use current ticks/div, do so **/
rgbTempo[1] = qpm * ticks / 24;
} else if (33 > qpm) /** Else if qpm is very small **/
if (26 > qpm) { /** approximate it **/
rgbTempo[0] = 31;
rgbTempo[1] = 33;
} else {
rgbTempo[0] = 31;
rgbTempo[1] = qpm * 31 / 24;
}
else if (6144 <= qpm) { /** Else if qpm is very big **/
rgbTempo[0] = 1; /** approximate it too **/
rgbTempo[1] = 255;
} else { /** Else look for closest fraction **/
int j, k, kMax;
double ratio, junk;
ratio = qpm / 24.0;
j = k = 791 / qpm + 1; /** I hope these constraints are Ok **/
kMax = 6143 / qpm;
while (k++ < kMax)
if (fabs(modf(ratio * k, &junk) - 0.5) >
fabs(modf(ratio * j, &junk) - 0.5))
j = k;
rgbTempo[0] = j;
rgbTempo[1] = j * ratio + 0.5;
}
}
/*
* PutpatternsPtunePfile: Given an output file and a tune, will output the
* tune as standard Protracker patterns. wMaxchan determines number of
* channels per division. wPatmax determines maximum number of patterns
* to write before terminating.
*
* date: 3/7/1994 - quiet samples now play sample 0 rather than sample 31
*/
int PutpatternsPtunePfile(Tune *ptune, FILE *pfile)
{
int iT, iT2, wPat = 0, cDiv, ipw, ipwMax, fGoing;
unsigned *pwLen, pwNote[3 * MAXCHANS], rgbTempo[2] = {6,125};
unsigned wNewtempo = 120;
unsigned long cDev = 0;
EI *pei;
pwLen = (unsigned *) calloc(MAXCHANS, sizeof(unsigned));
pei = PeiNextPtune(ptune, &fGoing); /** Get first event **/
ipw = wMaxchan;
ipwMax = wMaxchan * 3;
for (wPat = 0; fGoing; wPat++) { /** Loop until told to stop **/
if (wPat == wPatmax) { /** If pattern limit reached, stop **/
Error("Warning -- Pattern limit %d reached. Aborting!", wPatmax);
break;
}
for (cDiv = 64; cDiv--; ) { /** For each division in a pattern **/
memset(pwNote, 0, ipwMax * sizeof(unsigned)); /** Clear next notes **/
for (iT = wMaxchan; iT--; ) /** With any currently playing notes **/
if (pwLen[iT])
if (0 == --pwLen[iT]) { /** Check if just stopped **/
pwNote[iT2 = iT * 3] = 0; /** Yes.. store quiet command **/
pwNote[iT2 + 2] = 0xC00;
}
if (fGoing) { /** If still going in the tune **/
for (; NULL != pei; pei = pei->pei) /** For each event at this position **/
if (-1 != pei->pitch) { /** If note-event **/
if (-2 != pei->pitch) { /** which is valid **/
iT = ipwMax - 1; /*** Find channel to stick note ***/
for (; 0 < iT && 0xC00 != pwNote[iT]; iT -= 3);
if (0 > iT) {
for (iT = ipw; iT--; )
if (0 == pwLen[iT])
break;
if (0 > iT) {
for (iT = wMaxchan; iT-- > ipw; )
if (0 == pwLen[iT])
break;
if (0 > iT) {
iT2 = (unsigned) pei->effect / 2;
for (iT = wMaxchan; iT--; )
if (iT2 <= pwLen[iT])
break;
if (0 > iT) {
cDev++;
continue;
}
}
}
if (--ipw < 0)
ipw = wMaxchan - 1;
iT = iT * 3 + 2;
}
pwNote[iT -